/**
 *
 * User: Zhikang.Peng
 * Date: 2018/04/25 17:25
 */
package heap

import "gitee.com/thghh/jvmgo/classfile"

/**
maxStack 栈大小
maxLocals 局部变量表大小
argSlotCount 方法参数个数
*/
type Method struct {
	ClassMember
	maxStack                uint
	maxLocals               uint
	code                    []byte
	exceptionTable          ExceptionTable
	lineNumberTable         *classfile.LineNumberTableAttribute
	exceptions              *classfile.ExceptionsAttribute // RuntimeVisibleParameterAnnotations_attribute
	parameterAnnotationData []byte                         // AnnotationDefault_attribute
	annotationDefaultData   []byte
	parsedDescriptor        *MethodDescriptor
	argSlotCount            uint
}

var (
	_shimClass  = &Class{name: "~shim"}
	_returnCode = []byte{0xb1} // return
	_athrowCode = []byte{0xbf} // athrow

	_returnMethod = &Method{
		ClassMember: ClassMember{
			accessFlags: ACC_STATIC,
			name:        "<return>",
			class:       _shimClass,
		},
		code: _returnCode,
	}

	_athrowMethod = &Method{
		ClassMember: ClassMember{
			accessFlags: ACC_STATIC,
			name:        "<athrow>",
			class:       _shimClass,
		},
		code: _athrowCode,
	}
)

func newMethods(class *Class, cfMethods []*classfile.MemberInfo) []*Method {
	methods := make([]*Method, len(cfMethods))
	for i, cfMethod := range cfMethods {
		methods[i] = &Method{}
		methods[i].class = class
		methods[i].copyMemberInfo(cfMethod)
		methods[i].copyAttributes(cfMethod)
		md := parseMethodDescriptor(cfMethod.Descriptor())
		methods[i].calcArgSlotCount(md.parameterTypes)
		if methods[i].IsNative() {
			methods[i].injectCodeAttribute(md.returnType)
		}
	}
	return methods
}

func (self *Method) copyAttributes(cfMethod *classfile.MemberInfo) {
	codeAttr := cfMethod.CodeAttribute()
	if codeAttr != nil {
		self.maxStack = codeAttr.MaxStack()
		self.maxLocals = codeAttr.MaxLocals()
		self.code = codeAttr.Code()
		self.lineNumberTable = codeAttr.LineNumberTableAttribute()
		self.exceptionTable = newExceptionTable(codeAttr.ExceptionTable(), self.class.constantPool)
	}
	self.exceptions = cfMethod.ExceptionsAttribute()
	self.annotationData = cfMethod.RuntimeVisibleAnnotationsAttributeData()
	self.parameterAnnotationData = cfMethod.RuntimeVisibleParameterAnnotationsAttributeData()
	self.annotationDefaultData = cfMethod.AnnotationDefaultAttributeData()
}

/**
计算参数在局部变量表中占用个数
第一，long和double类型的参数要占用两个位置。
第二，对于实例方法，Java编译器会在参数列表的前面添加一个参数，这个隐藏的参数就是this引用.
第三,其他类型占用一个位置
*/
func (self *Method) calcArgSlotCount(parameterTypes []string) {
	for _, paramType := range parameterTypes {
		self.argSlotCount++
		if paramType == "J" || paramType == "D" {
			self.argSlotCount++
		}
	}
	// 实例方法
	if !self.IsStatic() {
		self.argSlotCount++ // `this` reference
	}
}

func (self *Method) IsSynchronized() bool {
	return 0 != self.accessFlags&ACC_SYNCHRONIZED
}
func (self *Method) IsBridge() bool {
	return 0 != self.accessFlags&ACC_BRIDGE
}
func (self *Method) IsVarargs() bool {
	return 0 != self.accessFlags&ACC_VARARGS
}
func (self *Method) IsNative() bool {
	return 0 != self.accessFlags&ACC_NATIVE
}
func (self *Method) IsAbstract() bool {
	return 0 != self.accessFlags&ACC_ABSTRACT
}
func (self *Method) IsStrict() bool {
	return 0 != self.accessFlags&ACC_STRICT
}

// getters
func (self *Method) MaxStack() uint {
	return self.maxStack
}

func (self *Method) MaxLocals() uint {
	return self.maxLocals
}

func (self *Method) Code() []byte {
	return self.code
}

func (self *Method) ParameterAnnotationData() []byte {
	return self.parameterAnnotationData
}

func (self *Method) AnnotationDefaultData() []byte {
	return self.annotationDefaultData
}

func (self *Method) ParsedDescriptor() *MethodDescriptor {
	return self.parsedDescriptor
}

func (self *Method) ArgSlotCount() uint {
	return self.argSlotCount
}

// 搜索异常处理表，如果能够找到对应的异常处理项，则返回它的handlerPc字段，否则返回–1
func (self *Method) FindExceptionHandler(exClass *Class, pc int) int {
	handler := self.exceptionTable.findExceptionHandler(exClass, pc)
	if handler != nil {
		return handler.handlerPc
	}
	return -1
}

func (self *Method) GetLineNumber(pc int) int {
	if self.IsNative() {
		return -2
	}
	if self.lineNumberTable == nil {
		return -1
	}
	return self.lineNumberTable.GetLineNumber(pc)
}

// 注入调用本地方法的字节码0xfe
func (self *Method) injectCodeAttribute(returnType string) {
	self.maxStack = 4
	self.maxLocals = self.argSlotCount
	switch returnType[0] {
	case 'V': // return
		self.code = []byte{0xfe, 0xb1}
	case 'D': // dreturn
		self.code = []byte{0xfe, 0xaf}
	case 'F': // freturn
		self.code = []byte{0xfe, 0xae}
	case 'J': // lreturn
		self.code = []byte{0xfe, 0xad}
	case 'L', '[': // areturn
		self.code = []byte{0xfe, 0xb0}
	default: // iretrun
		self.code = []byte{0xfe, 0xac}
	}
}

func (self *Method) isConstructor() bool {
	return !self.IsStatic() && self.name == "<init>"
}

func (self *Method) isClinit() bool {
	return self.IsStatic() && self.name == "<clinit>"
}

func ShimReturnMethod() *Method {
	return _returnMethod
}

// reflection
// 获取方法参数的类型
func (self *Method) ParameterTypes() []*Class {
	if self.argSlotCount == 0 {
		return nil
	}

	paramTypes := self.parsedDescriptor.parameterTypes
	paramClasses := make([]*Class, len(paramTypes))
	for i, paramType := range paramTypes {
		paramClassName := toClassName(paramType)
		paramClasses[i] = self.class.classLoader.LoaderClass(paramClassName)
	}
	return paramClasses
}

// 获取方法返回值得类型
func (self *Method) ReturnType() *Class {
	returnType := self.parsedDescriptor.returnType
	returnClassName := toClassName(returnType)
	return self.class.classLoader.LoaderClass(returnClassName)
}

// 获取方法抛出的异常的类型
func (self *Method) ExceptionTypes() []*Class {
	if self.exceptions == nil {
		return nil
	}

	exIndexTable := self.exceptions.ExceptionIndexTable()
	exClasses := make([]*Class, len(exIndexTable))
	cp := self.class.constantPool

	for i, exIndex := range exIndexTable {
		classRef := cp.GetConstant(uint(exIndex)).(*ClassRef)
		exClasses[i] = classRef.ResolvedClass()
	}

	return exClasses
}
